package cn.clq.ymcc.service.impl;

import cn.clq.ymcc.doc.CourseDoc;
import cn.clq.ymcc.dto.CourserSearchDto;
import cn.clq.ymcc.esmapper.HighlightResultMapper;
import cn.clq.ymcc.repository.CourseEsRepository;
import cn.clq.ymcc.result.PageList;
import cn.clq.ymcc.service.ICourseEsService;
import cn.clq.ymcc.vo.AggsBucket;
import cn.clq.ymcc.vo.AggsPageList;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author clqlongbao
 * @version 1.0
 * @description: TODO
 * @date 2022/7/24 17:38
 */
@Service
public class CourseEsServiceImpl implements ICourseEsService {
    @Autowired
    private CourseEsRepository courseEsRepository;
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    private HighlightResultMapper highlightResultMapper;
    /**
     * @description: 新增课程
     * @param: courseDoc
     * @return: void
     * @author clqlongbao
     * @date: 2022/7/25 17:28
     */
    @Override
    public void saveCourse(CourseDoc courseDoc) {
        courseEsRepository.save(courseDoc);
    }

    /**
     * @description: 删除课程
     * @param: courseDoc
     * @return: void
     * @author clqlongbao
     * @date: 2022/7/25 17:28
     */
    @Override
    public void deleteCourse(CourseDoc courseDoc) {
        courseEsRepository.delete(courseDoc);
    }


    /**
     * @description: 从ES分页查询课程
     * 基本思路：和链接池类似，需要先创建一个参数配置对象，设置各种参数，然后使用该参数配置对象生成实际的查询对象
     * @param: courseDoc
     * @return: cn.clq.ymcc.result.PageList<cn.clq.ymcc.doc.CourseDoc>
     * @author clqlongbao
     * @date: 2022/7/27 3:06
     */
    @Override
    public PageList<CourseDoc> search(CourserSearchDto dto) {
        //1.创建一个参数配置对象===================================
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        //2.封装排序条件==========================================
        if(StringUtils.hasLength(dto.getSortField())){
            // dto.getSortField()  xl rq  xp pl
            String sortField = null;
            //保证前端传递过来的字段和我们排序时的字段，是一致的，和CourseDoc中的字段保持一致
            switch (dto.getSortField().toLowerCase()){
                case "xl": sortField="saleCount";break;
                case "xp": sortField="onlineTime";break;
                case "pl": sortField="commentCount";break;
                case "jg": sortField="price";break;
                case "rq": sortField="viewCount";break;
                default: break;
            }
            if(sortField != null){
                if(StringUtils.hasLength(dto.getSortType())){
                    SortOrder sortOrder = dto.getSortType().equalsIgnoreCase("ASC") ? SortOrder.ASC : SortOrder.DESC;
                    builder.withSort(new FieldSortBuilder(sortField).order(sortOrder));
                }
            }
        }
        //3.封装分页查询相关的参数,0代表第一页======================
        builder.withPageable(PageRequest.of(dto.getPage()-1, dto.getRows()));
        //4.封装查询条件==========================================
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        //DSL查询(关键字模糊查询)
        if(StringUtils.hasLength(dto.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("name",dto.getKeyword()));
        }
        //DSL过滤(等值精确查询)
        if(dto.getCourseTypeId() != null){
            boolQuery.filter(QueryBuilders.termQuery("courseTypeId",dto.getCourseTypeId()));
        }
        if(StringUtils.hasLength(dto.getGradeName())){
            boolQuery.filter(QueryBuilders.termQuery("gradeName",dto.getGradeName()));
        }
        if(StringUtils.hasLength(dto.getChargeName())){
            boolQuery.filter(QueryBuilders.termQuery("chargeName",dto.getChargeName()));
        }
        if(dto.getPriceMin() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(dto.getPriceMin()));
        }
        if(dto.getPriceMax() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(dto.getPriceMax()));
        }

        /*
         *设置搜索关键字高亮
         *1.设置高亮样式，用<font>标签
         *2.准备mapper映射文件
         *3.修改最终的查询返回数据
         */
        builder.withHighlightFields(new HighlightBuilder.Field("name")
                                                        .preTags("<font style='color:red'>")
                                                        .postTags("</font>"));
        /*
         *聚合查询
         *1.设置聚合查询的条件
         *2.将查询结果封装成前端需要的格式
         */
        //设置难度等级和是否免费的聚合条件
        builder.addAggregation(AggregationBuilders.terms("aggsGradeName").field("gradeName"))
                .addAggregation(AggregationBuilders.terms("aggsChargeName").field("chargeName"));
        //5.搜索Es  ============================================
        builder.withQuery(boolQuery);
        NativeSearchQuery build = builder.build();
        //原生的返回结果
        //Page<CourseDoc> page = courseEsRepository.search(build);
        AggregatedPage<CourseDoc> page = elasticsearchRestTemplate.queryForPage(build, CourseDoc.class, highlightResultMapper);
        //将查询结果封装成前端需要的格式
        Map<String, Aggregation> map = page.getAggregations().asMap();
        ParsedStringTerms aggsGradeName = (ParsedStringTerms) map.get("aggsGradeName");
        ParsedStringTerms aggsChargeName = (ParsedStringTerms) map.get("aggsChargeName");
        //将list中的每一个元素，转换成我们需要的元素类型,并放到新的list中
        List< AggsBucket> aggsGradeBuckets = new ArrayList<>();
        aggsGradeName.getBuckets().forEach(bucket -> {
            String key = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            AggsBucket aggsBucket = new AggsBucket(key,docCount);
            aggsGradeBuckets.add(aggsBucket);
        });
        List< AggsBucket> aggsChargeBuckets = new ArrayList<>();
        aggsChargeName.getBuckets().forEach(bucket -> {
            String key = bucket.getKeyAsString();
            long docCount = bucket.getDocCount();
            AggsBucket aggsBucket = new AggsBucket(key,docCount);
            aggsChargeBuckets.add(aggsBucket);
        });
        //封装成最后的map
        Map<String, List<AggsBucket>> aggResult = new HashMap<>();
        aggResult.put("aggsGradeName",aggsGradeBuckets);
        aggResult.put("aggsChargeName",aggsChargeBuckets);

        //6.封装PageList返回 ===================================
        return new AggsPageList<>(page.getTotalElements(),page.getContent(),aggResult);
    }
}
